package edu.washington.cs.workflow.analysis.guistructure.uievents;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.swing.tree.TreeModel;

import org.uispec4j.Panel;
import org.uispec4j.Tree;
import org.uispec4j.UIComponent;

import edu.washington.cs.workflow.analysis.guistructure.uievents.UIEvent.WINDOW;
import edu.washington.cs.workflow.util.Utils;

public class TreeEvent extends AbstractUIEvent {
	
	private final Tree tree;
	private final UIAction action;
	private final String path;
	
	public TreeEvent(Tree tree, Panel panel, UIAction action) {
		this(tree, panel, action, randomPath(tree));
	}
	
	public TreeEvent(Tree tree, Panel panel, UIAction action, String path) {
		Utils.checkNotNull(tree);
		Utils.checkNotNull(panel);
		Utils.checkNotNull(action);
		Utils.checkNotNull(path);
		this.tree = tree;
		Utils.checkTrue(action.equals(UIAction.Click) || action.equals(UIAction.DoubleClick) || action.equals(UIAction.RightClick),
				"Illegal action: " + action);
		this.action = action;
		this.setCurrentPanel(panel);
		this.path = path;
	}
	
	public String getPath() {
		return path;
	}

	@Override
	public UIComponent getUIComponent() {
		return this.tree;
	}

	@Override
	public UIAction getAction() {
		return this.action;
	}
	
	@Override
	protected WINDOW probeWindowInfoInternal() {
		return WINDOW.NONE;
	}
	
	@Override
	public boolean canFireEvent() {
		return this.tree.isEnabled().isTrue();
	}

	@Override
	protected UIEventOutcome fireEventInternal() {
		//XXX do not consider pop up dialog yet
		UIEventOutcome outcome = new UIEventOutcome();
		Throwable error = null;
		try {
			if(this.action.equals(UIAction.Click)) {
//				System.out.println(this.path);
				this.tree.click(this.path);
			} else if (this.action.equals(UIAction.RightClick)) {
				this.tree.rightClick(this.path);
			} else if (this.action.equals(UIAction.DoubleClick)) {
				this.tree.doubleClick(this.path);
			} else {
				return null;
			}
		} catch (Throwable e) {
			error = e;
		}
		if(error != null) {
		    outcome.setError(error);
		}
		outcome.currentPanelClosed(super.isCurrentPanelClosed());
		return outcome;
	}
	
	@Override
	public String toString() {
		return "Tree ("  +  tree.getName() + ")" + this.action + " on path: " + tree.getAwtComponent().getModel().getRoot() + "/" + this.path;
	}
	
//	@Override
//	public String getDescription() {
//		return "Tree ("  +  tree.getName() + ")" + this.action + " on path: " + tree.getAwtComponent().getModel().getRoot() + "/" + this.path;
//	}
	
	public static String randomPath(Tree tree) {
		TreeModel treeModel = tree.getAwtComponent().getModel();
		Object root = treeModel.getRoot();
		
		Collection<Object> allObjs = new LinkedList<Object>();
		allObjs.add(new String(""));
		Collection<Object[]> relations = new LinkedList<Object[]>();
		
		//definitely no circle in a tree
		List<Object> queue = new LinkedList<Object>();
		queue.add(root);
		while(!queue.isEmpty()) {
			Object obj = queue.remove(0);
			//can not add root
			if(obj != root) {
			    allObjs.add(obj);
			}
			Utils.checkNotNull(obj);
			int count = treeModel.getChildCount(obj);
			if(count >0) {
				for(int i = 0 ; i < count; i++) {
					Object child = treeModel.getChild(obj, i);
					queue.add(child);
					if(obj != root) {
					    relations.add(new Object[]{obj, child});
					}
				}
			}
		}
		
		//randomly select an object and generate a path
		Object randomObj = Utils.randomElement(allObjs);
		String property = System.getProperty("uispec4j.tree.separator");
		String sep = property == null ? "/" : property;
		String path = randomObj.toString();
		
		while(true) {
			Object currObj = randomObj;
			Object parent = null;
			for(Object[] pair : relations) {
				if(pair[1] == currObj) {
					parent = pair[0];
					break;
				}
			}
			if(parent != null) {
				path = parent + sep + path;
				currObj = parent;
			} else {
				break;
			}
		}
		
		return path;
	}
}